import os
import json
import win32file
import win32con
import logging
import threading
import time
import pyodbc
import subprocess
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
import sys
import shutil

# 配置日志
logging.basicConfig(level=logging.ERROR, format="%(asctime)s - %(message)s", filename="file_watcher.log", filemode='w')

# 定义监听的更改类型
FILE_LIST_DIRECTORY = 0x0001

# 锁定
lock = threading.Lock()


def get_config_path(config_file):
    """获取配置文件路径，支持打包后的路径"""
    if getattr(sys, 'frozen', False):  # 检查是否为打包后的程序
        base_path = sys._MEIPASS  # 获取 PyInstaller 解包后的临时目录
        # 生成目标文件路径
        final_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), config_file)

        # 如果配置文件不存在，复制配置文件到当前工作目录
        if not os.path.exists(final_path):
            config_path_temp = os.path.join(base_path, config_file)
            if os.path.exists(config_path_temp):
                shutil.copy(config_path_temp, final_path)
                print(f"配置文件从临时目录复制到 {final_path}")
            else:
                raise FileNotFoundError(f"配置文件未找到: {config_path_temp}")
        return final_path  # 返回最终路径
    else:
        # 如果是未打包的程序，则直接返回相对路径
        return os.path.join(os.path.dirname(os.path.abspath(__file__)), config_file)


def load_config(config_file):
    """加载配置文件"""
    config_path = get_config_path(config_file)
    with open(config_path, 'r', encoding='utf-8') as f:
        return json.load(f)


def connect_to_network_share(network_path, username, password):
    """使用用户名和密码连接到网络共享"""
    cmd = f'net use {network_path} /user:{username} {password}'
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        if result.returncode != 0:
            logging.error(f"连接网络共享失败: {result.stderr.strip()}")
        else:
            logging.info(f"成功连接到网络共享: {network_path}")
    except UnicodeDecodeError as e:
        logging.error(f"Unicode 解码错误: {e}")


# SQL Server 数据库连接管理
class Database:
    def __init__(self, config):
        self.config = config

    def __enter__(self):
        self.conn = pyodbc.connect(
            f"DRIVER={{ODBC Driver 17 for SQL Server}};"
            f"SERVER={self.config['db_server']};"
            f"DATABASE={self.config['db_name']};"
            f"UID={self.config['db_username']};"
            f"PWD={self.config['db_password']}"
        )
        self.conn.autocommit = False
        self.cursor = self.conn.cursor()
        return self.cursor

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            logging.error(f"数据库操作异常: {exc_val}")
            self.conn.rollback()
        else:
            self.conn.commit()
        self.cursor.close()
        self.conn.close()


# 监听文件夹类
class DirectoryWatcher:
    def __init__(self, config):
        """初始化监听器"""
        self.directories_to_watch = config['folders_to_watch']
        self.filter_types = config['filter_types']
        self.buffer_size = config['buffer_size']
        self.recursive = config['recursive']
        self.executor = ThreadPoolExecutor(max_workers=5)
        self.pending_files = set()  # 使用 set 避免重复
        self.config = config

    def watch_directory_for_changes(self):
        """监听文件夹的变化并处理"""
        for path in self.directories_to_watch:
            threading.Thread(target=self._watch_single_directory, args=(path,), daemon=True).start()

    def _watch_single_directory(self, path_to_watch):
        """监听单个目录"""
        h_directory = win32file.CreateFile(
            path_to_watch,
            FILE_LIST_DIRECTORY,
            win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
            None,
            win32con.OPEN_EXISTING,
            win32con.FILE_FLAG_BACKUP_SEMANTICS,
            None,
        )
        logging.info(f"开始监听文件夹: {path_to_watch}")

        while True:
            results = win32file.ReadDirectoryChangesW(
                h_directory,
                self.buffer_size,
                self.recursive,
                win32con.FILE_NOTIFY_CHANGE_FILE_NAME | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
                None,
                None,
            )
            for action, file_name in results:
                full_path = os.path.join(path_to_watch, file_name)

                # 筛选文件类型
                if not any(full_path.endswith(ext) for ext in self.filter_types):
                    continue

                # 处理文件新增/重命名
                if action in {1, 5}:  # FILE_ACTION_ADDED 和 FILE_ACTION_RENAMED_NEW_NAME
                    with lock:
                        if full_path not in self.pending_files:
                            self.pending_files.add(full_path)
                    logging.info(f"发现文件变化: {full_path}")

    def process_pending_files(self):
        """处理待插入的文件"""
        while True:
            with lock:
                if self.pending_files:
                    files_to_process = list(self.pending_files)
                    self.pending_files.clear()
                else:
                    files_to_process = []

            if files_to_process:
                self.executor.submit(self._save_files_to_db, files_to_process)
            time.sleep(1)

    def _save_files_to_db(self, files):
        """批量保存文件到数据库"""
        with Database(self.config) as cursor:
            try:
                insert_query = f"""
                    INSERT INTO {self.config['db_table_name']} (binpath, binname)
                    SELECT ?, ? 
                    WHERE NOT EXISTS (SELECT 1 FROM {self.config['db_table_name']} WHERE binpath = ?)
                """
                data = []
                for f in set(files):
                    if not os.path.exists(f):  # 如果文件不存在，跳过
                        logging.warning(f"文件已不存在: {f}")
                        continue

                    file_name = os.path.basename(f)
                    file_base, _ = os.path.splitext(file_name)

                    # 判断文件名是否包含 'HW'
                    if 'HW' in file_base.upper():
                        bin_name = file_base.split("-")[-1]
                        logging.info(f"文件 {file_name} 包含 'HW'，提取部分为: {bin_name}")
                    else:
                        bin_name = file_base
                        logging.info(f"文件 {file_name} 不包含 'HW'，完整文件名为: {bin_name}")

                    data.append((os.path.abspath(f), bin_name, os.path.abspath(f)))

                if data:
                    cursor.fast_executemany = True
                    cursor.executemany(insert_query, data)
                    logging.info(f"成功插入 {len(data)} 条记录")
                else:
                    logging.info("没有数据需要插入")
            except Exception as e:
                logging.error(f"批量插入数据库时出错: {e}")


# 主函数
def main():
    config = load_config('config.json')

    for network_path in config['folders_to_watch']:
        username = config['username']
        password = config['password']
        connect_to_network_share(network_path, username, password)

    watcher = DirectoryWatcher(config)

    # 启动监听和文件处理线程
    threading.Thread(target=watcher.watch_directory_for_changes, daemon=True).start()
    threading.Thread(target=watcher.process_pending_files, daemon=True).start()

    print(f"程序启动成功，开始监听文件夹: {config['folders_to_watch']}, 启动时间{datetime.now()}")

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        logging.info("程序终止，退出监听。")


if __name__ == "__main__":
    main()